package utils

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"runtime"
	"sync"
)

//T
type T struct {
	sync.Mutex //互斥锁

	ch chan struct{} //获取事件channel

	closeChan chan struct{} //关闭事件channel

	p *os.Process //记录process对象

	f func(chan struct{}) error //执行任务
}

//T实例化
func NewT() *T {
	return newT(nil)
}

//T实例化
func newT(f func(chan struct{}) error) *T {
	t := &T{
		Mutex:     sync.Mutex{},
		ch:        make(chan struct{}, 1),
		closeChan: make(chan struct{}),
		f:         f,
	}
	if f == nil {
		t.f = t.DefaultFunc
	}
	return t
}

//Task接口
type RunTask interface {
	AddTask()
	RunTask()
}

//添加任务
func (t *T) AddTask() {
	if len(t.ch) == 1 {
		return //代表已经有任务了
	}
	t.Lock()
	defer t.Unlock()
	if len(t.ch) == 1 {
		return //代表已经有任务了
	}
	t.ch <- struct{}{}
}

//启动任务
func (t *T) RunTask() {
	fmt.Println("进入")
	// 这里做的make 是用于关闭上一个执行的任务
	ch := make(chan struct{})
	// run服务
	go t.f(ch)
	for {
		_, ok := <-t.ch
		if !ok {
			return
		}
		ch <- struct{}{} // 等待上一个关闭
		<-t.closeChan
		go t.f(ch)
	}
}

//默认的StartFunction
func (t *T) DefaultFunc(ch chan struct{}) error {
	var buildCmd *exec.Cmd
	var cmd *exec.Cmd

	//监测系统是否有编译环境
	_, err := exec.LookPath("go")
	if err != nil {
		return err
	}

	//build
	switch runtime.GOOS {
	case "windows":
		buildCmd = exec.Command("go", "build", "-o", "server.exe", "main.go")
	default:
		buildCmd = exec.Command("go", "build", "-o", "server", "main.go")
	}
	//cmd= exec.Command("go","run","main.go")
	err = buildCmd.Run()
	if err != nil {
		return err
	}
	fmt.Println("build 执行完成")

	//执行
	switch runtime.GOOS {
	case "windows":
		cmd = exec.Command("server.exe")
	default:
		cmd = exec.Command("./server")
	}

	//开始执行任务
	ctx, cancel := context.WithCancel(context.Background())
	err = t.echo(cmd, ctx)
	<-ch
	//回收资源
	fmt.Println("pid:", t.p.Pid, "->kill")
	err = t.p.Kill()
	cancel()
	//发送关闭完成信号
	t.closeChan <- struct{}{}
	return err
}

//封装回显(就是显示正在执行的批处理命令及执行的结果等)
func (t *T) echo(cmd *exec.Cmd, ctx context.Context) error {
	var buffer bytes.Buffer
	stdoutIn, _ := cmd.StdoutPipe()

	var errStdout error
	stdout := io.MultiWriter(os.Stdout, &buffer)
	err := cmd.Start()
	if err != nil {
		return err
	}
	go func(ctx context.Context) {
		_, errStdout = io.Copy(stdout, stdoutIn)
		select {
		case <-ctx.Done():
			return
		default:
		}
	}(ctx)
	t.p = cmd.Process
	fmt.Println("pid", t.p.Pid)
	go func() {
		_ = cmd.Wait()
		if errStdout != nil {
			fmt.Printf("failed to capture stdout or stderr\n")
		}
		fmt.Printf("#{string(buffer.Bytes())}\n")
		select {
		case <-ctx.Done():
			//_=os.Stdout.Close()
			return
		default:
		}
	}()
	return nil
}
